package cn.juque.datapipeline.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.juque.common.constants.StringConstants;
import cn.juque.common.constants.YesOrNoEnum;
import cn.juque.common.exception.AppException;
import cn.juque.datapipeline.api.enums.DataPipelineMsgEnum;
import cn.juque.datapipeline.bo.WriteParamBO;
import cn.juque.datapipeline.entity.TaskFieldMappingInfo;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author nuoka
 * @version 1.0.0
 * <li></li>
 * @date 2022/8/23 19:00
 **/
@Slf4j
public class PreparedStatementUtil {

    private PreparedStatementUtil() {
    }

    public static Long batchInsert(WriteParamBO paramBO) throws SQLException {
        String format = "insert into {}({}) values ({})";
        List<String> nameList = CollUtil.newArrayList();
        List<String> inFieldList = CollUtil.newArrayList();
        List<String> outValueList = CollUtil.newArrayList();
        paramBO.getFieldList().forEach(t -> {
            inFieldList.add(t.getSourceField());
            outValueList.add(StringConstants.QUESTION);
            nameList.add(t.getTargetField());
        });
        String sql = CharSequenceUtil.format(
                format,
                paramBO.getTableName(),
                CollUtil.join(nameList, StringConstants.COMMA),
                CollUtil.join(outValueList, StringConstants.COMMA));
        Connection connection = (Connection) paramBO.getConnection();
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            connection.setAutoCommit(false);
            for (Map<String, Object> data : paramBO.getDataList()) {
                for (int i = 0; i < inFieldList.size(); i++) {
                    statement.setObject(i + 1, data.get(inFieldList.get(i)));
                }
                statement.addBatch();
            }
            return commit(paramBO, statement);
        }
    }

    public static Long batchUpdate(WriteParamBO paramBO) throws SQLException {
        String format = "update {} set {} where {}=?";
        Optional<TaskFieldMappingInfo> idOption = paramBO.getFieldList().stream()
                .filter(t -> YesOrNoEnum.YES.getCode().equals(t.getIdFlag()))
                .findFirst();
        if (!idOption.isPresent()) {
            throw new AppException(DataPipelineMsgEnum.ID_FIELD_MISSING);
        }
        TaskFieldMappingInfo idField = idOption.get();
        List<String> inFieldList = CollUtil.newArrayList();
        List<String> outValueList = CollUtil.newArrayList();
        paramBO.getFieldList().stream()
                .filter(t -> !YesOrNoEnum.YES.getCode().equals(t.getIdFlag()))
                .forEach(t -> {
                    inFieldList.add(t.getSourceField());
                    outValueList.add(t.getTargetField() + "=?");
                });
        String sql = CharSequenceUtil.format(
                format,
                paramBO.getTableName(),
                CollUtil.join(outValueList, StringConstants.COMMA),
                idField.getTargetField());
        Connection connection = (Connection) paramBO.getConnection();
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            connection.setAutoCommit(false);
            for (Map<String, Object> data : paramBO.getDataList()) {
                for (int i = 0; i < inFieldList.size(); i++) {
                    statement.setObject(i + 1, data.get(inFieldList.get(i)));
                }
                // 配置主键
                statement.setObject(inFieldList.size() + 1, data.get(idField.getTargetField()));
                statement.addBatch();
            }
            return commit(paramBO, statement);
        }
    }

    public static Long batchDelete(WriteParamBO paramBO) throws SQLException {
        String format = "delete from {} where {}=?";
        Optional<TaskFieldMappingInfo> idOption = paramBO.getFieldList().stream()
                .filter(t -> YesOrNoEnum.YES.getCode().equals(t.getIdFlag()))
                .findFirst();
        if (!idOption.isPresent()) {
            throw new AppException(DataPipelineMsgEnum.ID_FIELD_MISSING);
        }
        TaskFieldMappingInfo idFieldBO = idOption.get();
        String sql = CharSequenceUtil.format(
                format,
                paramBO.getTableName(),
                idFieldBO.getTargetField());
        Connection connection = (Connection) paramBO.getConnection();
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            connection.setAutoCommit(false);
            for (Map<String, Object> data : paramBO.getDataList()) {
                // 配置主键
                statement.setObject(1, data.get(idFieldBO.getTargetField()));
                statement.addBatch();
            }
            return commit(paramBO, statement);
        }
    }

    private static Long commit(WriteParamBO paramBO, PreparedStatement statement) {
        try {
            int[] result = statement.executeBatch();
            Connection connection = (Connection) paramBO.getConnection();
            connection.commit();
            // 重置并关闭相关操作
            statement.clearBatch();
            statement.close();
            connection.setAutoCommit(true);
            return (long) result.length;
        } catch (Exception e) {
            log.error("采数异常，文件:{}, sql:{}", paramBO.getName(), statement, e);
            throw new AppException(DataPipelineMsgEnum.FILE_EXECUTE_ERROR);
        }
    }
}
